#include "config.h"

#include <dirent.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdarg.h>

#define DBG_SUBSYS S_LIBSTORAGE

#include "sysy_lib.h"
#include "cache.h"
#include "get_version.h"
#include "cluster.h"
#include "lichstor.h"
#include "lichstor.h"
#include "metadata.h"
#include "configure.h"
#include "job_dock.h"
#include "chunk.h"
#include "ynet_rpc.h"
#include "net_global.h"
#include "file_table.h"
#include "lich_md.h"
#include "simplefs.h"
#include "ylog.h"
#include "dbg.h"
#include "lichbd.h"

int pfs_init()
{
        int ret;

        ret = filetable_init();
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

static int __fs_open(const char *path, int flag, int *fd)
{
        int ret;
        fileid_t fid;

        (void) flag;

        ret = stor_lookup1("default", path, &fid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = filetable_open(fd, &fid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

static int __fs_create(const char *path, int flag, int mode, int *fd)
{
        int ret;
        fileid_t parentid, fid;
        char name[MAX_NAME_LEN];
        setattr_t setattr;

        (void) flag;

        ret = stor_splitpath("default", path, &parentid, name);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        memset(&setattr, 0x0, sizeof(setattr));
        setattr.mode.set_it = 1;
        setattr.mode.mode = mode;

        ret = stor_mkvol(&parentid, name, &setattr, &fid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = filetable_open(fd, &fid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int pfs_open(const char *path, int flag, mode_t mode)
{
        int ret, fd;

        ret = __fs_open(path, flag, &fd);
        if (unlikely(ret)) {
                if (ret == ENOENT) {
                        ret = __fs_create(path, flag, mode, &fd);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                } else
                        GOTO(err_ret, ret);
        } else {
                if ((flag & O_CREAT) && (flag & O_EXCL)) {
                        ret = EEXIST;
                        GOTO(err_fd, ret);
                }
        }

        return fd;
err_fd:
        filetable_close(fd);
err_ret:
        return -ret;
}

int pfs_create(const char *path, mode_t mode)
{
        int ret, fd;

        ret =  __fs_create(path, 0, mode, &fd);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return fd;
err_ret:
        return -ret;
}

int pfs_pread(int fd, void *buf, size_t size, off_t off)
{
        int ret;
        int read_size;
        fileid_t fid;

        ret = filetable_get(fd, &fid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        read_size = stor_pread("default", &fid, buf, size, off);

        return read_size;
err_ret:
        return -ret;
}

int pfs_pread_with_path(const char *path, void *buf, size_t size, off_t off)
{
        int ret, retry = 0;
        off_t left, offset;
        lichbd_ioctx_t ioctx;
        int localize = 0;

retry1:
        ret = lichbd_connect("default", path, &ioctx, 0);
        if (unlikely(ret)) {
                if (ret == EAGAIN) {
                        USLEEP_RETRY(err_ret, ret, retry1, retry, 50, (100 * 1000));
                } else
                        GOTO(err_ret, ret);
        }

        offset = 0;
        left = size;
        while (left > 0) {
                size = left < MAX_BUF_LEN ? left : MAX_BUF_LEN;

        retry2:
                ret = lichbd_pread(&ioctx, buf + offset, size, off + offset, localize);
                if (ret < 0) {
                        ret = -ret;
                        if (ret == EAGAIN) {
                                USLEEP_RETRY(err_ret, ret, retry2, retry, 50, (100 * 1000));
                        } else
                                GOTO(err_ret, ret);
                }

                YASSERT((uint64_t)ret == size);
                offset += size;
                left -= size;
        }

        return size;
err_ret:
        return -ret;
}

int pfs_pwrite(int fd, const void *buf, size_t size, off_t off)
{
        int ret;
        fileid_t fid;
        int retry = 0;

        ret = filetable_get(fd, &fid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

retry:
        ret = stor_pwrite("default", &fid, buf, size, off);
        if (unlikely(ret)) {
                if (ret == EAGAIN) {
                        USLEEP_RETRY(err_ret, ret, retry, retry, 50, (100 * 1000));
                } else
                        GOTO(err_ret, ret);
        }

        ret = size;
err_ret:
        return ret;
}

int pfs_pwrite_with_path(const char *path, const void *buf, size_t size, off_t off)
{
        int ret, retry = 0;
        lichbd_ioctx_t ioctx;

retry1:
        ret = lichbd_connect("default", path, &ioctx, O_CREAT);
        if (unlikely(ret)) {
                if (ret == EAGAIN) {
                        USLEEP_RETRY(err_ret, ret, retry1, retry, 50, (100 * 1000));
                } else
                        GOTO(err_ret, ret);
        }

retry2:
        ret = lichbd_pwrite(&ioctx, buf, size, off, 0);
        if (unlikely(ret)) {
                if (ret == EAGAIN) {
                        USLEEP_RETRY(err_ret, ret, retry2, retry, 50, (100 * 1000));
                } else
                        GOTO(err_ret, ret);
        }

        return size;
err_ret:
        return -ret;
}

int pfs_close(int fd)
{
        return filetable_close(fd);
}


int pfs_getattr(const char *path, struct stat *stbuf) {
        int ret;
        fileid_t fid;

        ret = stor_lookup1("default", path, &fid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = stor_getattr("default", &fid, stbuf);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int pfs_readdir(const char *path, off_t offset, void **_de, int *_delen) {
        int ret;
        fileid_t fid;
        uuid_t _uuid;
        char uuid[MAX_NAME_LEN] = {};

        ret = stor_lookup1("default", path, &fid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        uuid_generate(_uuid);
        uuid_unparse(_uuid, uuid);
        ret = stor_listpool_open(&fid, uuid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = stor_listpool(&fid, uuid, offset, _de, _delen);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = stor_listpool_close(&fid, uuid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int pfs_mkdir(const char *path, mode_t mode) {
        int ret;
        char name[MAX_NAME_LEN];
        fileid_t fid;
        setattr_t setattr;

        ret = stor_splitpath("default", path, &fid, name);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        memset(&setattr, 0x0, sizeof(setattr));
        setattr.mode.set_it = 1;
        setattr.mode.mode = mode;

        ret = stor_mkpool(&fid, name, &setattr, NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;

}

int pfs_unlink(const char *path) {
        int ret;
        char name[MAX_NAME_LEN];
        fileid_t fid;

        ret = stor_splitpath("default", path, &fid, name);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = stor_rmvol(&fid, name, 0);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int pfs_rmdir(const char *path) {
        int ret;
        char name[MAX_NAME_LEN];
        fileid_t fid;

        ret = stor_splitpath("default", path, &fid, name);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = stor_rmpool("default", &fid, name);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int pfs_chmod(const char *path, mode_t mode) {
        int ret;
        fileid_t fid;

        ret = stor_lookup1("default", path, &fid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = stor_chmod("default", &fid, mode);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int pfs_chown(const char *path, uid_t uid, gid_t gid) {
        int ret;
        fileid_t fid;

        ret = stor_lookup1("default", path, &fid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = stor_chown("default", &fid, uid, gid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int pfs_truncate(const char *path, off_t size) {
        int ret;
        fileid_t fid;

        ret = stor_lookup1("default", path, &fid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = stor_truncate("default", &fid, size);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int pfs_statfs(const char *path, struct statvfs *stbuf) {
        int ret;
        fileid_t fid;

        ret = stor_lookup1("default", path, &fid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = stor_statvfs("default", &fid, stbuf);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int pfs_utimens(const char *path, const time_t atime, const time_t mtime, const time_t ctime) {
        int ret;
        fileid_t fid;

        (void) ctime;

        ret = stor_lookup1("default", path, &fid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = stor_utime("default", &fid, atime, mtime);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int pfs_access(const char *path, int mask) {
        (void) path;
        (void) mask;
        return 0;
}

int pfs_readlink(const char *path, char *buf, size_t size) {
        (void) path;
        (void) buf;
        (void) size;
        return 0;
}

int pfs_mknod(const char *path, mode_t mode, dev_t rdev) {
        (void) path;
        (void) mode;
        (void) rdev;
        return 0;
}

int pfs_symlink(const char *from, const char *to) {
        (void) from;
        (void) to;
        return 0;
}

int pfs_rename(const char *from, const char *to) {
        (void) from;
        (void) to;
        return 0;
}

int pfs_link(const char *from, const char *to) {
        (void) from;
        (void) to;
        return 0;
}
